﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Components;

namespace WorkflowDesigner
{
    public class Connector : Canvas
    {
        #region fields
        private bool trackingMouseMove = false;
        private readonly Color strokeColor;
        private Path connectionPath;
        private Polygon arrow;
        private DateTime LastMouseClick = DateTime.Now;
        //private readonly Node fromNode;
        //private Point fromPort;
        //private Point toPort;
        #endregion

        #region props
        public bool IsErrorOutput { get; protected set; }
        public Port FromPort { get; set; }
        public Port ToPort { get; set; }
        #endregion

        #region ctor
        public Connector(Port fromPort, Port toPort)
        {
            this.Loaded += new RoutedEventHandler(Connector_Loaded);
            strokeColor = Color.FromArgb(0xaa, 0, 0, 0);
            if (fromPort.PortType == PortType.SuccessOutput)
                strokeColor = Colors.Green;
            else if (fromPort.PortType == PortType.ErrorOutput)
                strokeColor = Colors.Red;
            this.FromPort = fromPort;
            this.ToPort = toPort;
            if (fromPort.PortType == PortType.ErrorOutput)
                this.IsErrorOutput = true;
            else
                this.IsErrorOutput = false;
        }

        #endregion

        #region load
        void Connector_Loaded(object sender, RoutedEventArgs e)
        {
            this.connectionPath = new Path();
            this.arrow=new Polygon();
            this.UpdateConnectionPath(this.connectionPath, this.arrow);
            this.connectionPath.MouseLeftButtonDown += new MouseButtonEventHandler(connectionLine_MouseLeftButtonDown);
            this.connectionPath.MouseLeftButtonUp += new MouseButtonEventHandler(connectionLine_MouseLeftButtonUp);
            this.connectionPath.MouseMove += new MouseEventHandler(connectionLine_MouseMove);
            this.Children.Add(this.connectionPath);
            this.Children.Add(this.arrow);
        }
        #endregion

        #region mouse event handlers
        void connectionLine_MouseMove(object sender, MouseEventArgs e)
        {
            if(trackingMouseMove)
            {
                //connectionLine.X2 = e.GetPosition(this).X;
                //connectionLine.Y2 = e.GetPosition(this).Y;
                Panel parentPanel = (Panel) this.Parent;
                for(int i=0;i<parentPanel.Children.Count;i++)
                {
                    if(parentPanel.Children[i] is Node)
                    {
                        Node node = (Node) parentPanel.Children[i];
                        double xScale = 1, yScale = 1;
                        if(Application.Current.Host.Content.IsFullScreen)
                        {
                            xScale = Application.Current.Host.Content.ActualWidth/parentPanel.ActualWidth;
                            yScale = Application.Current.Host.Content.ActualHeight/parentPanel.ActualHeight;
                        }
                        if(node.Contains(new Point(
                            e.GetPosition(this).X * xScale,
                            e.GetPosition(this).Y * yScale)))
                            node.Highlight(true);
                        else 
                            node.Highlight(false);
                    }
                }
            }
        }

        void connectionLine_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
        {
            if(trackingMouseMove)
            {
                FrameworkElement fe = sender as FrameworkElement;
                fe.ReleaseMouseCapture();
                trackingMouseMove = false;
                // hide this line
                //connectionLine.Visibility = System.Windows.Visibility.Collapsed;
                Panel parentPanel = (Panel) this.Parent;
                for(int i=0;i<parentPanel.Children.Count;i++)
                {
                    if(parentPanel.Children[i] is Node)
                    {
                        Node node = (Node) parentPanel.Children[i];
                        //double xScale = 1, yScale = 1;
                        //if(Application.Current.Host.Content.IsFullScreen)
                        //{
                        //    xScale = Application.Current.Host.Content.ActualWidth/parentPanel.ActualWidth;
                        //    yScale = Application.Current.Host.Content.ActualHeight/parentPanel.ActualHeight;
                        //}
                        if(node.Highlighted)
                        {
                            node.Highlight(false);
                        }
                    }
                }
            }
        }

        void connectionLine_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
        {
            if (new TimeSpan(DateTime.Now.Ticks - this.LastMouseClick.Ticks).Seconds < 2)
            {
                // double click
                this.FromPort.OwnerNode.Disconnect(this);
                this.LastMouseClick = DateTime.Now;
                return;
            }
            else
            {
                // single click
                
                Panel parentPanel = (Panel)this.Parent;
                if (parentPanel == null)
                    return;
                int topMost = 0, zOrder;
                for (int i = 0; i < parentPanel.Children.Count; i++)
                {
                    if (parentPanel.Children[i] is Node)
                    {
                        Node node = (Node)parentPanel.Children[i];
                        zOrder = (int)node.GetValue(Canvas.ZIndexProperty);
                        if (zOrder > topMost)
                            topMost = zOrder;
                    }
                }
                //connectionLine.SetValue(Canvas.ZIndexProperty, topMost + 1);
                //connectionLine.X2 = e.GetPosition(this).X;
                //connectionLine.Y2 = e.GetPosition(this).Y;
                trackingMouseMove = true;
                FrameworkElement fe = sender as FrameworkElement;
                if (fe != null)
                    fe.CaptureMouse();

                this.LastMouseClick = DateTime.Now;
            }
        }
        #endregion

        #region public methods
        public void Delete()
        {
            Panel parentPanel = (Panel)this.Parent;
            parentPanel.Children.Remove(connectionPath);
        }

        public void RedrawConnection()
        {
            if(this.connectionPath ==null)
                this.connectionPath=new Path();
            this.UpdateConnectionPath(this.connectionPath, this.arrow);
        }
        #endregion

        #region draw
        private void UpdateConnectionPath(Path path, Polygon toArrow)
        {
            if (this.FromPort == null || this.ToPort == null)
                return;
            Point ts = new Point(
                this.FromPort.Position.X + this.FromPort.PortSize.Width/2,
                this.FromPort.Position.Y + this.FromPort.PortSize.Height);
            if (this.FromPort.PortType == PortType.ErrorOutput)
                ts = new Point(
                    this.FromPort.Position.X + this.FromPort.PortSize.Width,
                    this.FromPort.Position.Y + this.FromPort.PortSize.Height/2);
            Point te = new Point(
                this.ToPort.Position.X + this.ToPort.PortSize.Width/2,
                this.ToPort.Position.Y);
            GeometryGroup group=new GeometryGroup();
            double delta = 25;
            if (ts.X < te.X &&
                ts.Y + delta * 2 < te.Y &&
                 this.FromPort.PortType == PortType.SuccessOutput)
            {
                // A
                Point t1 = new Point(ts.X, (ts.Y + te.Y)/2);
                Point t2 = new Point(te.X, (ts.Y + te.Y)/2);
                group.Children.Add(new LineGeometry() {StartPoint=ts, EndPoint = t1});
                group.Children.Add(new LineGeometry() {StartPoint = t1, EndPoint = t2});
                group.Children.Add(new LineGeometry() {StartPoint = t2, EndPoint = te});
            }
            else if (ts.X  < te.X &&
                ts.Y + delta * 2 < te.Y &&
                this.FromPort.PortType == PortType.ErrorOutput)
            {
                // A
                Point t1=new Point(te.X, ts.Y);
                group.Children.Add(new LineGeometry()
                                       {StartPoint=ts, EndPoint = t1});
                group.Children.Add(new LineGeometry()
                                       {StartPoint = t1, EndPoint = te});
            }
            else if (ts.X > te.X &&
                ts.Y + delta * 2 < te.Y &&
                this.FromPort.PortType == PortType.SuccessOutput)
            {
                // B
                Point t1=new Point(ts.X, (ts.Y + te.Y)/2);
                Point t2=new Point(te.X, (ts.Y +te.Y)/2);
                group.Children.Add(new LineGeometry() { StartPoint = ts, EndPoint = t1 });
                group.Children.Add(new LineGeometry() { StartPoint = t1, EndPoint = t2 });
                group.Children.Add(new LineGeometry() { StartPoint = t2, EndPoint = te });
            }
            else if (ts.X + delta > te.X &&
                ts.Y + delta < te.Y &&
                this.FromPort.PortType == PortType.ErrorOutput)
            {
                // B
                Point t1 = new Point(ts.X + delta, ts.Y);
                Point t2 = new Point(ts.X + delta, (ts.Y + te.Y)/2);
                Point t3=new Point(te.X, (ts.Y+te.Y)/2);
                group.Children.Add(new LineGeometry() { StartPoint = ts, EndPoint = t1 });
                group.Children.Add(new LineGeometry() { StartPoint = t1, EndPoint = t2 });
                group.Children.Add(new LineGeometry() { StartPoint = t2, EndPoint = t3 });
                group.Children.Add(new LineGeometry() { StartPoint = t3, EndPoint = te });
            }
            else if (ts.X > te.X &&
                ts.Y + delta * 2 > te.Y &&
                this.FromPort.PortType == PortType.SuccessOutput)
            {
                // C
                Point t1=new Point(ts.X,ts.Y + delta);
                Point t2 = new Point((ts.X + te.X)/2, ts.Y + delta);
                Point t3=new Point((ts.X +te.X)/2,te.Y-delta);
                Point t4=new Point(te.X,te.Y-delta);
                group.Children.Add(new LineGeometry() { StartPoint = ts, EndPoint = t1 });
                group.Children.Add(new LineGeometry() { StartPoint = t1, EndPoint = t2 });
                group.Children.Add(new LineGeometry() { StartPoint = t2, EndPoint = t3 });
                group.Children.Add(new LineGeometry() { StartPoint = t3, EndPoint = t4 });
                group.Children.Add(new LineGeometry() { StartPoint = t4, EndPoint = te });
            }
            else if (ts.X + delta > te.X &&
                ts.Y + delta > te.Y &&
                this.FromPort.PortType == PortType.ErrorOutput)
            {
                // C
                Point t1=new Point(ts.X+delta,ts.Y);
                Point t2=new Point(ts.X+delta,te.Y -delta);
                Point t3=new Point(te.X,te.Y-delta);
                group.Children.Add(new LineGeometry() { StartPoint = ts, EndPoint = t1 });
                group.Children.Add(new LineGeometry() { StartPoint = t1, EndPoint = t2 });
                group.Children.Add(new LineGeometry() { StartPoint = t2, EndPoint = t3 });
                group.Children.Add(new LineGeometry() { StartPoint = t3, EndPoint = te });
            }
            else if (ts.X < te.X &&
                ts.Y + delta * 2 > te.Y &&
                this.FromPort.PortType == PortType.SuccessOutput)
            {
                // D
                Point t1=new Point(ts.X,ts.Y+delta);
                Point t2 = new Point((ts.X + te.X)/2, ts.Y + delta);
                Point t3=new Point((ts.X+te.X)/2,te.Y - delta);
                Point t4=new Point(te.X,te.Y - delta);
                group.Children.Add(new LineGeometry() { StartPoint = ts, EndPoint = t1 });
                group.Children.Add(new LineGeometry() { StartPoint = t1, EndPoint = t2 });
                group.Children.Add(new LineGeometry() { StartPoint = t2, EndPoint = t3 });
                group.Children.Add(new LineGeometry() { StartPoint = t3, EndPoint = t4 });
                group.Children.Add(new LineGeometry() { StartPoint = t4, EndPoint = te });
            }
            else if (ts.X + delta < te.X &&
                ts.Y + delta > te.Y &&
                this.FromPort.PortType == PortType.ErrorOutput)
            {
                // D
                Point t1=new Point((ts.X+te.X)/2,ts.Y);
                Point t2=new Point((ts.X+te.X)/2,te.Y-delta);
                Point t3=new Point(te.X,te.Y -delta);
                group.Children.Add(new LineGeometry() { StartPoint = ts, EndPoint = t1 });
                group.Children.Add(new LineGeometry() { StartPoint = t1, EndPoint = t2 });
                group.Children.Add(new LineGeometry() { StartPoint = t2, EndPoint = t3 });
                group.Children.Add(new LineGeometry() { StartPoint = t3, EndPoint = te });
            }
            else if (ts.X == te.X && ts.Y + delta < te.Y - delta &&
                this.FromPort.PortType == PortType.SuccessOutput)
            {
                group.Children.Add(new LineGeometry() { StartPoint = ts, EndPoint = te });
            }
            path.Data = group;
            path.Stroke = new SolidColorBrush(strokeColor);
            path.StrokeThickness = Node.StrokeThickness;

            toArrow.Fill = new SolidColorBrush(strokeColor);
            toArrow.Points.Clear();
            toArrow.Points.Add(new Point(this.ToPort.Position.X + this.ToPort.PortSize.Width/2, this.ToPort.Position.Y));
            toArrow.Points.Add(new Point(this.ToPort.Position.X + this.ToPort.PortSize.Width / 2 - 5, this.ToPort.Position.Y - 10));
            toArrow.Points.Add(new Point(this.ToPort.Position.X + this.ToPort.PortSize.Width / 2 + 5, this.ToPort.Position.Y - 10));
        }
        #endregion
    }
}
